#define IO_PIC 0x20 /* 8259A PIC port 0x20 0x21 */
#define IRQ_OFFSET 32 /* 8259A PIC starting interrupt vector number */

.text
.code16 // Assembly generates 16 bit machine instructions
.start16:
  cli // Clear the interrupt bit in the flag register, informing the processor not to respond to interrupts until 64 bit mode

// 1.[Operating in real mode] Copy the E820 record from the BIOS data area to the kernel image area
  mov $e820_entry, %di // Set the starting position for record storage
  xor %ebx, %ebx // init 0, index of e820 records, auto increase by bios
e820_rd_entry: // Start of loop
  mov $0xe820, %eax // Set interrupt number
  mov $20, %ecx // Set to read one E820 record(20B) at a time
  int $0x15 // Execute interrupt service num 0x15 for detecting memory

  incb e820_nr_entry // Record count self increasing by 1
  add $20, %di // Set the record storage position to be moved back by 20B(one record size)

  cmp $0, %ebx // Judging index==0, indicates that one round of traversal is complete
  jne e820_rd_entry // Continue loop

// [debug only] print e820 record
//  mov $0x3f8, %dx
//  mov e820_nr_entry, %eax // Take the value at label e820_nr_entry address to eax
//  out %eax, %dx
//
//  mov $e820_entry, %ebx // Take the base address value of label array(e820_entry) to ebx
//  mov $0, %esi // Index of array(e820_entry)
//debug_foreach_e820_array:
//  mov (%ebx, %esi, 4), %eax // Take the value at the i-th(%esi) address of the array(e820_entry) to eax
//  out %eax, %dx
//
//  add $1, %esi // Increase index by 1
//  cmp $20, %esi // Max index = (20B * 4 items) / 4B
//  jb debug_foreach_e820_array

// 2.Initialize 8259A PIC(Programmable interrupt Controller)
  // ICW1
  mov $0x13, %al
  mov $(IO_PIC), %dx
  out %al, %dx // write to 0x20 port
  // ICW2
  mov $(IRQ_OFFSET), %al
  mov $(IO_PIC + 1), %dx
  out %al, %dx // write to 0x21 port
  // ICW3 ignored
  // ICW4
  mov $0x1, %al
  mov $(IO_PIC + 1), %dx
  out %al, %dx // write to 0x21 port

// 3.Read the mode information from the graphics card BIOS and save it to 0x4000
  mov $vesa_mode_info, %di // Backfilling location for returning information
  mov $0x4f01, %ax         // Specify the type of information obtained as pattern information
  int $0x10                // Execute interrupt service num 0x10 for obtaining pattern information

// 4.Switch from 16-bit real mode to 32-bit protected mode
  lgdt gdtr // Notify processor of segment descriptor table address

  mov %cr0, %eax // Set the 0th bit PE of the CR0 register to 1 and enable protection mode
  or $0x1, %eax
  mov %eax, %cr0
 
  ljmpl $0x8, $0x20000 // Jump to the 32-bit protected mode code segment section
 
gdt: // Definition of segment descriptor table
  .quad 0x0000000000000000 // 0st descriptor - No need, x86 convention is 0
  .quad 0x00c09a00000007ff // 1st descriptor - kernel code segment
  .quad 0x00c09200000007ff // 2nd descriptor - kernel data segment
gdt_end:

gdtr: // Segment descriptor table address definition, total of 6 bytes
  .word gdt_end - gdt // Low 16 bits - Table length - Starting and ending offset difference of 2 bytes
  .word gdt, 0x1 // High 32-bit - Table Address 2 2-Bytes

// E820 records stored in kernel image area
.org 0x3000
e820_nr_entry:
  .long 0 // 4B, Total number of E820 records
e820_entry:
  .fill 1024, 1, 0 // Storage location for E820 records
// total 4 records: low -> high (20B)
// 0       0    9fc00     0     1
// 9fc00   0    400       0     2
// f0000   0    10000     0     2
// 100000  0    ff00000   0     1

// Storing BIOS mode information for graphics cards
// - Framebuffer pyhsical address | Screen resolution | Color depth ...
.org 0x4000
vesa_mode_info:
  .fill 256, 1, 0